#include "snetwork/NluHttpParse.h"   
#include "standard/CThrowException.h"   

namespace Sock
{   
    CNluHttpParse *CNluHttpParse::m_instance = NULL;

    CNluHttpParse *CNluHttpParse::instance(void*)
    {
        if (m_instance==NULL) {
            m_instance = new CNluHttpParse();
        }
        return m_instance;
    }
    
    CNluHttpParse::CNluHttpParse()
    {

    }
    
    // parse start
    int CNluHttpParse::ParseSwStart()
    {        
        m_r->request_start = m_p;                
        if (*m_p == CR || *m_p == LF) {
            return HTTP_BREAK;
        }

        if ((*m_p < 'A' || *m_p > 'Z') && *m_p != '_') {
            return NL_HTTP_PARSE_INVALID_METHOD;
        }

        m_state = sw_method;
        return HTTP_OK;
    }
    
    // parse method
    int CNluHttpParse::ParseSwMethod(char *m)
    {
        int i = 0;
        
        m_p--;        
        m_r->method_end = m_p - 1;
        
        while (*m_p != ' ' && i<10) {            
            m[i] = *m_p;                    
            i++;            
            m_p++;
        }                        
        
        switch (i) {

        case 3:
            if (NL_HTTP_str3_cmp(m, 'G', 'E', 'T', ' ')) {
                this->m_r->method = NL_HTTP_GET;
                break;
            }

            if (NL_HTTP_str3_cmp(m, 'P', 'U', 'T', ' ')) {
                this->m_r->method = NL_HTTP_PUT;
                break;
            }

            break;

        case 4:
            if (m[1] == 'O') {

                if (NL_HTTP_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
                    this->m_r->method = NL_HTTP_POST;
                    break;
                }

                if (NL_HTTP_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
                    this->m_r->method = NL_HTTP_COPY;
                    break;
                }

                if (NL_HTTP_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
                    this->m_r->method = NL_HTTP_MOVE;
                    break;
                }

                if (NL_HTTP_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
                    this->m_r->method = NL_HTTP_LOCK;
                    break;
                }

            } else {

                if (NL_HTTP_str4cmp(m, 'H', 'E', 'A', 'D')) {
                    this->m_r->method = NL_HTTP_HEAD;
                    break;
                }
            }

            break;

        case 5:
            if (NL_HTTP_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
                this->m_r->method = NL_HTTP_MKCOL;
            }

            if (NL_HTTP_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
                this->m_r->method = NL_HTTP_PATCH;
            }

            if (NL_HTTP_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
                this->m_r->method = NL_HTTP_TRACE;                
            }

            break;

        case 6:
            if (NL_HTTP_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
                this->m_r->method = NL_HTTP_DELETE;
                break;
            }

            if (NL_HTTP_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
                this->m_r->method = NL_HTTP_UNLOCK;
                break;
            }

            break;

        case 7:
            if (NL_HTTP_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
            {
                this->m_r->method = NL_HTTP_OPTIONS;
            }

            break;

        case 8:
            if (NL_HTTP_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D')) {
                this->m_r->method = NL_HTTP_PROPFIND;
            }

            break;

        case 9:
            if (NL_HTTP_str9cmp(m, 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H')) {
                this->m_r->method = NL_HTTP_PROPPATCH;
            }

            break;
        }
        
        //llog("m_p : %s", m_p);// /?t=8a&a=11 HTTP/1.1
        m_state = sw_spaces_before_uri;
        
        return HTTP_OK;
    }
    
    int CNluHttpParse::ParseSwSpacesBeforeUri()
    {

        if (*m_p == '/') {
            m_r->uri_start = m_p;
            m_state = sw_after_slash_in_uri;
            return HTTP_BREAK;
        }

        char c = (char) (*m_p | 0x20);
        if (c >= 'a' && c <= 'z') {
            m_r->schema_start = m_p;
            m_state = sw_schema;
            return HTTP_BREAK;
        }

        switch (*m_p) {
        case ' ':
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;                
    }
    
    int CNluHttpParse::ParseSwSchema()
    {
        char c = (char) (*m_p | 0x20);
        if (c >= 'a' && c <= 'z') {
            return HTTP_BREAK;
        }

        switch (*m_p) {
        case ':':
            m_r->schema_end = m_p;
            m_state = sw_schema_slash;
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        
        return HTTP_OK;
    }     
    
    int CNluHttpParse::ParseSwSchemaSlash()
    {
        switch (*m_p) {
        case '/':
            m_state = sw_schema_slash_slash;
            return HTTP_BREAK;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        
        return HTTP_OK;
    }            

    int CNluHttpParse::ParseSwSchemaSlashSlash() 
    {
        switch (*m_p) {
        case '/':
            m_r->host_start = m_p + 1;
            m_state = sw_host;
            return HTTP_BREAK;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        
        return HTTP_OK;
    }       

    int CNluHttpParse::ParseSwHost()
    {
        char c = (char) (*m_p | 0x20);
        if (c >= 'a' && c <= 'z') {
            return HTTP_BREAK;
        }

        if ((*m_p >= '0' && *m_p <= '9') || *m_p == '.' || *m_p == '-') {
            return HTTP_BREAK;
        }

        m_r->host_end = m_p;

        switch (*m_p) {
        case ':':
            m_state = sw_port;
            break;
        case '/':
            m_r->uri_start = m_p;
            m_state = sw_after_slash_in_uri;
            break;
        case ' ':
            /*
             * use single "/" from request line to preserve pointers,
             * if request line will be copied to large client buffer
             */
            m_r->uri_start = m_r->schema_end + 1;
            m_r->uri_end = m_r->schema_end + 2;
            m_state = sw_host_http_09;
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }

        return HTTP_OK;            
    }


    int CNluHttpParse::ParseSwPort()
    {
        if (*m_p >= '0' && *m_p <= '9') {
            return HTTP_BREAK;
        }

        switch (*m_p) {
        case '/':
            m_r->port_end = m_p;
            m_r->uri_start = m_p;
            m_state = sw_after_slash_in_uri;
            break;
        case ' ':
            m_r->port_end = m_p;
            /*
             * use single "/" from request line to preserve pointers,
             * if request line will be copied to large client buffer
             */
            m_r->uri_start = m_r->schema_end + 1;
            m_r->uri_end = m_r->schema_end + 2;
            m_state = sw_host_http_09;
            
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwHostHttp09()
    {

        switch (*m_p) {
        case ' ':
            break;
        case CR:
            m_r->http_minor = 9;
            m_state = sw_almost_done;
            break;
        case LF:
            m_r->http_minor = 9;
            throw CThrowException("done");
        case 'H':
            m_r->http_protocol = m_p;
            m_state = sw_http_H;
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwAfterSlashInUrl()
    {
        char ch = *m_p;        
        //llog("ParseSwAfterSlashInUrl ch=%c", ch);
        if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
            m_state = sw_check_uri;
            return HTTP_BREAK;
        }
        //llog("m_p : %s", m_p); 

        switch (ch) {
        case ' ':
            m_r->uri_end = m_p;
            m_state = sw_check_uri_http_09;
            break;
        case CR:
            m_r->uri_end = m_p;
            m_r->http_minor = 9;
            m_state = sw_almost_done;
            break;
        case LF:
            m_r->uri_end = m_p;
            m_r->http_minor = 9;
            throw CThrowException("done");
        case '.':
            m_r->complex_uri = 1;
            m_state = sw_uri;
            break;
        case '%':
            m_r->quoted_uri = 1;
            m_state = sw_uri;
            break;
        case '/':
            m_r->complex_uri = 1;
            m_state = sw_uri;
            break;
        case '?':
            m_r->args_start = m_p + 1;
            m_state = sw_uri;
            break;
        case '#':
            m_r->complex_uri = 1;
            m_state = sw_uri;
            break;
        case '+':
            m_r->plus_in_uri = 1;
            break;
        case '\0':
            return NL_HTTP_PARSE_INVALID_REQUEST;
        default:
            m_state = sw_check_uri;
            break;
        }
        
        //llog("m_state : %d", m_state);
        return HTTP_BREAK;
    }        
    
    int CNluHttpParse::ParseSwCheckUri()
    {
        char ch = *m_p;

        if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
            return HTTP_BREAK;
        }

        switch (ch) {
        case '/':
            m_r->uri_ext = NULL;
            m_state = sw_after_slash_in_uri;
            break;
        case '.':
            m_r->uri_ext = m_p + 1;
            break;
        case ' ':
            m_r->uri_end = m_p;
            m_state = sw_check_uri_http_09;
            break;
        case CR:
            m_r->uri_end = m_p;
            m_r->http_minor = 9;
            m_state = sw_almost_done;
            break;
        case LF:
            m_r->uri_end = m_p;
            m_r->http_minor = 9;
            throw CThrowException("done");
        case '%':
            m_r->quoted_uri = 1;
            m_state = sw_uri;
            break;
        case '?':
            m_r->args_start = m_p + 1;
            m_state = sw_uri;
            break;
        case '#':
            m_r->complex_uri = 1;
            m_state = sw_uri;
            break;
        case '+':
            m_r->plus_in_uri = 1;
            break;
        case '\0':
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        
        return HTTP_BREAK;
    }    
    
    int CNluHttpParse::ParseSwCheckUriHttp09()
    {
        switch (*m_p) {
        case ' ':
            break;
        case CR:
            m_r->http_minor = 9;
            m_state = sw_almost_done;
            break;
        case LF:
            m_r->http_minor = 9;
            throw CThrowException("done");
        case 'H':
            m_r->http_protocol = m_p;
            m_state = sw_http_H;
            break;
        default:
            m_r->space_in_uri = 1;
            m_state = sw_check_uri;
            break;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwUri()
    {

        char ch = *m_p;
        if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
            return HTTP_BREAK;
        }
        //llog("Uri ch=%c", ch);
        switch (ch) {
        case ' ':
            m_r->uri_end = m_p;
            m_state = sw_http_09;
            break;
        case CR:
            m_r->uri_end = m_p;
            m_r->http_minor = 9;
            m_state = sw_almost_done;
            break;
        case LF:
            m_r->uri_end = m_p;
            m_r->http_minor = 9;
            throw CThrowException("done");
        case '#':
            m_r->complex_uri = 1;
            break;
        case '\0':
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwHttp09()
    {        
        //llog("Uri m_p=%c", *m_p);
        switch (*m_p) {
        case ' ':
            break;
        case CR:
            m_r->http_minor = 9;
            m_state = sw_almost_done;
            break;
        case LF:
            m_r->http_minor = 9;
            throw CThrowException("done");
        case 'H':
            m_r->http_protocol = m_p;
            m_state = sw_http_H;
            break;
        default:
            m_r->space_in_uri = 1;
            m_state = sw_uri;
            break;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwHttpH()
    {
        switch (*m_p) {
        case 'T':
            m_state = sw_http_HT;
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwHttpHT()
    {
        switch (*m_p) {
        case 'T':
            m_state = sw_http_HTT;
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwHttpHTT()
    {
        switch (*m_p) {
        case 'P':
            m_state = sw_http_HTTP;
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwHttpHTTP()
    {
        switch (*m_p) {
        case '/':
            m_state = sw_first_major_digit;
            break;
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwFirstMajorDigit()
    {
        if (*m_p < '1' || *m_p > '9') {
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        m_r->http_major = *m_p - '0';

        m_state = sw_major_digit;
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwMajorDigit()
    {
        //llog("m_r->http_major:%d", m_r->http_major);
            
        if (*m_p == '.') {
            m_state = sw_first_minor_digit;
            return HTTP_BREAK;
        }
        if (*m_p < '0' || *m_p > '9') {
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }

        m_r->http_major = m_r->http_major * 10 + *m_p - '0';
        
        return HTTP_BREAK;

    }
    
    int CNluHttpParse::ParseSwFirstMinorDigit()
    {
        if (*m_p < '0' || *m_p > '9') {
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }

        m_r->http_minor = *m_p - '0';
        m_state = sw_minor_digit;
        //llog("m_r->http_minor:%d", m_r->http_minor);
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwMinorDigit()
    {

        if (*m_p == CR) {
            m_state = sw_almost_done;
            return HTTP_BREAK;
        }

        if (*m_p == LF) {
            throw CThrowException("done");
        }

        if (*m_p == ' ') {
            m_state = sw_spaces_after_digit;
            return HTTP_BREAK;
        }

        if (*m_p < '0' || *m_p > '9') {
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }

        m_r->http_minor = m_r->http_minor * 10 + *m_p - '0';
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwSpacesAfterDigit()
    {
        switch (*m_p) {
        case ' ':
            break;
        case CR:
            m_state = sw_almost_done;
            break;
        case LF:
            throw CThrowException("done");
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
        return HTTP_BREAK;
    }
    
    int CNluHttpParse::ParseSwAlmostDone()
    {
        m_r->request_end = m_p - 1;
        switch (*m_p) {
        case LF:
            throw CThrowException("done");
        default:
            return NL_HTTP_PARSE_INVALID_REQUEST;
        }
    }
            

    /**
     *   GET /s?tn=monline_dg&ie=utf-8&bs=18%E5%A4%A7&f=8&rsv_bp=1&wd=AGAIN&rsv_n=2 HTTP/1.1
     *   Host: www.baidu.com
     *   User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0
     *   Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*\/*;q=0.8
     *   Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
     *   Accept-Encoding: gzip, deflate
     *   Connection: keep-alive
     *   Referer: http://www.baidu.com/baidu?wd=18%B4%F3&tn=monline_dg
     *   Cookie: BAIDUID=3D811F02B46C01E1ABE409E03AFFD89D:FG=1; MCITY=-131%3A; BDRCVFR[eHt_ClL0b_s]=mk3SLVN4HKm
     *   Cache-Control: max-age=0
     *
     */    
    //n_rbArr
    int CNluHttpParse::ParseRequestLine(nl_http_request_t *rr, http_buf_t *b)
    {
        int retstate;
        char  m[10];
        this->m_r = rr;                                
        
        memset(m, 0, sizeof(m));
        m_state = sw_start;
    
        try {
            for (m_p = b->pos; m_p < b->last; m_p++) {//m_p < b->last
                
                switch (m_state) {
                
                /* HTTP methods: GET, HEAD, POST */
                case sw_start:
                    retstate = ParseSwStart(); 
                    break;
                    
                case sw_method: 
                    retstate = ParseSwMethod(m);
                    break;
                    
                case sw_spaces_before_uri:
                    retstate = ParseSwSpacesBeforeUri();
                    break;
                    
                case sw_schema:
                    retstate = ParseSwSchema();
                    break;
                
                case sw_schema_slash:
                    retstate = ParseSwSchemaSlash();
                    break;
                                
                case sw_schema_slash_slash:
                    retstate = ParseSwSchemaSlashSlash();
                    break;
                    
                case sw_host:
                    retstate = ParseSwHost();
                    break;
                    
                case sw_port:
                    retstate = ParseSwPort();
                    break; 
                                    
                /* space+ after "http://host[:port] " */
                case sw_host_http_09:
                    ParseSwHostHttp09();
                    break;    

                /* check "/.", "//", "%", and "\" (Win32) in URI */
                case sw_after_slash_in_uri:
                    ParseSwAfterSlashInUrl();
                    break;                 

                /* check "/", "%" and "\" (Win32) in URI */
                case sw_check_uri:
                    ParseSwCheckUri();
                    break; 
                
                /* space+ after URI */
                case sw_check_uri_http_09:
                    ParseSwCheckUriHttp09();
                    break;                                         

                /* URI */
                case sw_uri:
                    ParseSwUri();   
                    break;         

                /* space+ after URI */
                case sw_http_09:
                    ParseSwHttp09();
                    break;             

                case sw_http_H:
                    ParseSwHttpH(); 
                    break;            

                case sw_http_HT:
                    ParseSwHttpHT();
                    break;             

                case sw_http_HTT:
                    ParseSwHttpHTT();
                    break;             

                case sw_http_HTTP:
                    ParseSwHttpHTTP(); 
                    break;            

                /* first digit of major HTTP version */
                case sw_first_major_digit:
                    ParseSwFirstMajorDigit();  
                    break;           

                /* major HTTP version or dot */
                case sw_major_digit:
                    ParseSwMajorDigit();
                    break; 
                                
                /* first digit of minor HTTP version */
                case sw_first_minor_digit:
                    ParseSwFirstMinorDigit();
                    break;             

                /* minor HTTP version or end of request line */
                case sw_minor_digit:
                    ParseSwMinorDigit();
                    break; 

                case sw_spaces_after_digit:
                    ParseSwSpacesAfterDigit(); 
                    break;            

                /* end of request line */
                case sw_almost_done:
                    ParseSwAlmostDone();
                    break; 
                }
            }
            
            b->pos = m_p;
            m_r->state = m_state;

            return HTTP_AGAIN;
        }
        catch (CThrowException e) {
        
            b->pos = m_p + 1;

            if (m_r->request_end == NULL) {
                m_r->request_end = m_p;
            }

            m_r->http_version = m_r->http_major * 1000 + m_r->http_minor;
            m_r->state = sw_start;

            if (m_r->http_version == 9 && m_r->method != NL_HTTP_GET) {
                return NL_HTTP_PARSE_INVALID_09_METHOD;
            }
        }        
        //llog("m_p:%s", m_p);

        return HTTP_OK;
    }
    
    
    int CNluHttpParse::ParseHeaderLine(http_buf_t *b, nl_http_request_t *r, 
            unsigned int allow_underscores)
    {
        char            c, ch, *p;
        unsigned int    hash, i;
        enum {
            sw_start = 0,
            sw_name,
            sw_space_before_value,
            sw_value,
            sw_space_after_value,
            sw_ignore_line,
            sw_almost_done,
            sw_header_almost_done
        } state;

        /* the last '\0' is not needed because string is zero terminated */

        static char  lowcase[] =
            "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
            "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
            "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
            "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
            "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
            "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
            "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
            "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

        state = sw_start;
        hash = r->header_hash;
        i = r->lowcase_index;

        for (p = b->pos; p < b->last; p++) {
            ch = *p;
            
            //llog("state :%d, ch :%c", state, ch);
            switch (state) {
            /* first char */
            case sw_start:
                r->header_name_start = p;
                r->invalid_header = 0;

                switch (ch) {
                case CR://13
                    r->header_end = p;
                    state = sw_header_almost_done;
                    break;
                case LF://10
                    r->header_end = p;
                    goto header_done;
                default:
                    state = sw_name;

                    c = lowcase[ch];
                    if (c) {
                        hash = http_hash(0, c);
                        r->lowcase_header[0] = c;
                        i = 1;
                        break;
                    }

                    r->invalid_header = 1;
                    break;
                }
                break;

            /* header name */
            case sw_name:
                c = lowcase[ch];

                if (c) {
                    hash = http_hash(hash, c);
                    r->lowcase_header[i++] = c;
                    i &= (NL_HTTP_LC_HEADER_LEN - 1);
                    break;
                }

                if (ch == '_') {
                    if (allow_underscores) {
                        hash = http_hash(hash, ch);
                        r->lowcase_header[i++] = ch;
                        i &= (NL_HTTP_LC_HEADER_LEN - 1);

                    } else {
                        r->invalid_header = 1;
                    }

                    break;
                }

                if (ch == ':') {
                    r->header_name_end = p;
                    state = sw_space_before_value;
                    break;
                }

                if (ch == CR) {
                    r->header_name_end = p;
                    r->header_start = p;
                    r->header_end = p;
                    state = sw_almost_done;
                    break;
                }

                if (ch == LF) {
                    r->header_name_end = p;
                    r->header_start = p;
                    r->header_end = p;
                    goto done;
                }

                /* IIS may send the duplicate "HTTP/1.1 ..." lines */
                if (ch == '/'
                    && r->upstream
                    && p - r->header_name_start == 4
                    && strncmp(r->header_name_start, "HTTP", 4) == 0)
                {
                    state = sw_ignore_line;
                    break;
                }

                r->invalid_header = 1;

                break;

            /* space* before header value */
            case sw_space_before_value:
                switch (ch) {
                case ' ':
                    break;
                case CR:
                    r->header_start = p;
                    r->header_end = p;
                    state = sw_almost_done;
                    break;
                case LF:
                    r->header_start = p;
                    r->header_end = p;
                    goto done;
                default:
                    r->header_start = p;
                    state = sw_value;
                    break;
                }
                break;

            /* header value */
            case sw_value:
                switch (ch) {
                case ' ':
                    r->header_end = p;
                    state = sw_space_after_value;
                    break;
                case CR:
                    r->header_end = p;
                    state = sw_almost_done;
                    break;
                case LF:
                    r->header_end = p;
                    goto done;
                }
                break;

            /* space* before end of header line */
            case sw_space_after_value:
                switch (ch) {
                case ' ':
                    break;
                case CR:
                    state = sw_almost_done;
                    break;
                case LF:
                    goto done;
                default:
                    state = sw_value;
                    break;
                }
                break;

            /* ignore header line */
            case sw_ignore_line:
                switch (ch) {
                case LF:
                    state = sw_start;
                    break;
                default:
                    break;
                }
                break;

            /* end of header line */
            case sw_almost_done:                
                switch (ch) {
                case LF:
                    goto done;
                case CR:
                    break;
                default:
                    return NL_HTTP_PARSE_INVALID_HEADER;
                }
                break;

            /* end of header */
            case sw_header_almost_done:
                switch (ch) {
                case LF:
                    goto header_done;
                default:
                    return NL_HTTP_PARSE_INVALID_HEADER;
                }
            }
        }

        b->pos = p;
        r->state = state;
        r->header_hash = hash;
        r->lowcase_index = i;

        return HTTP_AGAIN;

done:

        b->pos = p + 1;
        r->state = sw_start;
        r->header_hash = hash;
        r->lowcase_index = i;

        return HTTP_OK;

header_done:

        b->pos = p + 1;
        r->state = sw_start;

        return NL_HTTP_PARSE_HEADER_DONE;
    }
            
}    
